#include "preHeader.h"
#include "MapServerMaster.h"
#include "MapServer.h"
#include "Cross/CrossServer.h"
#include "Session/GateServerListener.h"
#include "Session/GateServerSessionHandler.h"
#include "Session/GameServerSessionHandler.h"
#include "Session/InstancePacketHandler.h"
#include "Session/PlayerPacketHandler.h"
#include "Session/DBPSessionHandler.h"
#include "Session/GateServerMgr.h"
#include "Session/GameServerMgr.h"
#include "Session/DBPServerMgr.h"
#include "Session/MapServerPacketHandler.h"
#include "Session/CrossServerPacketHandler.h"
#include "Session/PacketDispatcher.h"
#include "Map/InstanceMgr.h"
#include "Map/SceneMgr.h"
#include "Object/PlayerMgr.h"
#include "Loot/LootMgr.h"
#include "Spell/SpellMgr.h"
#include "Quest/QuestMgr.h"
#include "Cross/PlayBossMgr.h"

MapServerMaster::MapServerMaster()
{
}

MapServerMaster::~MapServerMaster()
{
}

bool MapServerMaster::InitSingleton()
{
	LuaMgr::newInstance();
	PacketDispatcher::newInstance();
	MapServerPacketHandler::newInstance();
	CrossServerPacketHandler::newInstance();
	GateServerSessionHandler::newInstance();
	GameServerSessionHandler::newInstance();
	InstancePacketHandler::newInstance();
	PlayerPacketHandler::newInstance();
	DBPSessionHandler::newInstance();
	MysqlDatabasePool::newInstance();
	DatabaseMgr::newInstance();
	ShellOfGameConfig::newInstance();
	GateServerListener::newInstance();
	GateServerMgr::newInstance();
	GameServerMgr::newInstance();
	DBPServerMgr::newInstance();
	InstanceMgr::newInstance();
	SceneMgr::newInstance();
	PlayerMgr::newInstance();
	LootMgr::newInstance();
	SpellMgr::newInstance();
	QuestMgr::newInstance();
	CrossServer::newInstance();
	MapServer::newInstance();
	PlayBossMgr::newInstance();
	return true;
}

void MapServerMaster::FinishSingleton()
{
	LuaMgr::deleteInstance();
	PacketDispatcher::deleteInstance();
	MapServerPacketHandler::deleteInstance();
	CrossServerPacketHandler::deleteInstance();
	GateServerSessionHandler::deleteInstance();
	GameServerSessionHandler::deleteInstance();
	InstancePacketHandler::deleteInstance();
	PlayerPacketHandler::deleteInstance();
	DBPSessionHandler::deleteInstance();
	MysqlDatabasePool::deleteInstance();
	DatabaseMgr::deleteInstance();
	ShellOfGameConfig::deleteInstance();
	GateServerListener::deleteInstance();
	GateServerMgr::deleteInstance();
	GameServerMgr::deleteInstance();
	DBPServerMgr::deleteInstance();
	InstanceMgr::deleteInstance();
	SceneMgr::deleteInstance();
	PlayerMgr::deleteInstance();
	LootMgr::deleteInstance();
	SpellMgr::deleteInstance();
	QuestMgr::deleteInstance();
	CrossServer::deleteInstance();
	MapServer::deleteInstance();
	PlayBossMgr::deleteInstance();
}

bool MapServerMaster::InitDBPool()
{
	const auto& cfg = GetConfig();

	if (!sMysqlDatabasePool.InitWorldDB(
		cfg.GetString("WORLD_DB", "HOST", "127.0.0.1"),
		cfg.GetInteger("WORLD_DB", "PORT", 3306),
		cfg.GetString("WORLD_DB", "USER", "viewer"),
		cfg.GetString("WORLD_DB", "PASSWORD", "123456"),
		cfg.GetString("WORLD_DB", "DATABASE", "mmorpg_world"),
		1, 0))
	{
		return false;
	}

	if (!sMysqlDatabasePool.InitActvtDB(
		cfg.GetString("ACTVT_DB", "HOST", "127.0.0.1"),
		cfg.GetInteger("ACTVT_DB", "PORT", 3306),
		cfg.GetString("ACTVT_DB", "USER", "viewer"),
		cfg.GetString("ACTVT_DB", "PASSWORD", "123456"),
		cfg.GetString("ACTVT_DB", "DATABASE", "mmorpg_actvt"),
		1, 0))
	{
		return false;
	}

	return true;
}

bool MapServerMaster::LoadDBData()
{
	const auto& cfg = GetConfig();
	sDBMgr.SetLang(cfg.GetInteger("OTHER", "LANG", 0));

	DatabaseMgr::SetupActvtDB();
	DatabaseMgr::SetupWorldDB();
	if (!sDBMgr.AsyncLoadAllTables(16)) {
		return false;
	}

	ThreadSafeQueue<std::function<bool()>> tasks;
	tasks.Enqueue([]() {
		return sShellOfGameConfig.LoadNewCfgInst();
	});
	tasks.Enqueue([]() {
		return sLootMgr.LoadLootRelation();
	});
	tasks.Enqueue([]() {
		return sSpellMgr.LoadSpellRelation();
	});
	tasks.Enqueue([]() {
		return sQuestMgr.LoadQuests();
	});
	auto threads = std::min(tasks.GetSize(), OS::GetProcNum());
	if (!sDBMgr.WaitFinishTasks(tasks, threads)) {
		return false;
	}

	return true;
}

bool MapServerMaster::StartServices()
{
	const auto& cfg = GetConfig();
	sGameServerMgr.AddGameServer(
		cfg.GetString("GAME_SERVER", "HOST", "127.0.0.1"),
		cfg.GetString("GAME_SERVER", "PORT", "9997"), 0);
	sDBPServerMgr.AddDBPServer(
		cfg.GetString("DBP_SERVER", "HOST", "127.0.0.1"),
		cfg.GetString("DBP_SERVER", "PORT", "9990"));
	if (!CrossServer()) {
		ELOG("--- CrossServer() failed.");
		return false;
	}

	if (!sGateServerListener.Start()) {
		ELOG("--- sGateServerListener.Start() failed.");
		return false;
	}

	if (!sInstanceMgr.Start()) {
		ELOG("--- sInstanceMgr.Start() failed.");
		return false;
	}

	if (!sMapServer.Start()) {
		ELOG("--- sMapServer.Start() failed.");
		return false;
	}

	if (!sCrossServer.Start()) {
		ELOG("--- sCrossServer.Start() failed.");
		return false;
	}

	return true;
}

void MapServerMaster::StopServices()
{
	sMapServer.OfflineAllPlayer();
	sGateServerListener.Stop();
	sInstanceMgr.Stop();
	sMapServer.Stop();
	sCrossServer.Stop();
}

void MapServerMaster::Tick()
{
	sGameServerMgr.CheckConnections();
	sDBPServerMgr.CheckConnections();
	sMysqlDatabasePool.GetActvtDB()->CheckConnections();
	sMysqlDatabasePool.GetWorldDB()->CheckConnections();
}

std::string MapServerMaster::GetConfigFile()
{
	return "etc/MapServer.conf";
}

size_t MapServerMaster::GetDeferAsyncServiceCount()
{
	return GetConfig().GetInteger("OTHER", "DEFER_ASYNC_THREAD", 1);
}

size_t MapServerMaster::GetAsyncServiceCount()
{
	return GetConfig().GetInteger("OTHER", "ASYNC_THREAD", 1);
}

size_t MapServerMaster::GetIOServiceCount()
{
	return GetConfig().GetInteger("OTHER", "IO_THREAD", 1);
}

bool MapServerMaster::CrossServer()
{
	KeyFile cfg;
	if (!ParseConfigFile(cfg, "etc/CrossServer.conf")) {
		// we allow not have the config file.
		return true;
	}

	auto n = cfg.GetInteger("CROSS_SERVER", "CROSS_SERVER_COUNT", 0);
	if (n == 0) {
		// forbid cross server.
		return true;
	}

	std::ostringstream s;
	for (int i = 1; i <= n; ++i, s.str({})) {
		s << "CROSS_SERVER_" << i;
		const std::string csGroup = s.str();
		sGameServerMgr.AddGameServer(
			cfg.GetString(csGroup.c_str(), "GAME_HOST", "127.0.0.1"),
			cfg.GetString(csGroup.c_str(), "GAME_PORT", "9997"),
			cfg.GetInteger(csGroup.c_str(), "SERVICE", 1));
		sDBPServerMgr.AddDBPServer(
			cfg.GetString(csGroup.c_str(), "DBP_HOST", "127.0.0.1"),
			cfg.GetString(csGroup.c_str(), "DBP_PORT", "9990"));
	}

	return true;
}
